package com.motu.vertx.module.utility.ClassReload;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.instrument.*;
import java.security.ProtectionDomain;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public final class ClassReloader
{

	private static Instrumentation inst = null;

	public ClassReloader()
	{
	}

	public static void premain(String agentArgs, Instrumentation i)
	{
		inst = i;
		System.out.println("=========premain============");
		i.addTransformer(new ClassFileTransformer() {

			@Override
			public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
					ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
				// TODO Auto-generated method stub
//				System.out.println("preMain Class:"+className);
				return classfileBuffer;
			}
		});
	}

	public static void reload(Class cls, File file)
		throws IOException, ClassNotFoundException, UnmodifiableClassException
	{
		byte code[] = loadBytes(cls, file);
		if (code == null)
		{
			throw new IOException("Unknown File");
		} else
		{
			ClassDefinition def = new ClassDefinition(cls, code);
			inst.redefineClasses(new ClassDefinition[] {
				def
			});
			//System.err.println((new StringBuilder(String.valueOf(cls.getName()))).append(" reloaded").toString());//sjh
			return;
		}
	}

	private static byte[] loadBytes(Class cls, File file)
		throws IOException, ClassNotFoundException
	{
		String name = file.getName();
		if (name.endsWith(".jar"))
			return loadBytesFromJarFile(cls, file);
		if (name.endsWith(".class"))
			return loadBytesFromClassFile(file);
		else
			return null;
	}

	private static byte[] loadBytesFromClassFile(File classFile)
		throws IOException
	{
		byte buffer[];
		BufferedInputStream bis;
		buffer = new byte[(int)classFile.length()];
		FileInputStream fis = new FileInputStream(classFile);
		bis = new BufferedInputStream(fis);
		try {
			bis.read(buffer);
		} catch (IOException e) {
			throw e;
		} finally {
			bis.close();
		}
		return buffer;
	}

	private static byte[] loadBytesFromJarFile(Class cls, File file)
		throws IOException, ClassNotFoundException
	{
		byte buffer[];
		BufferedInputStream bis;
		JarFile jarFile = new JarFile(file);
		String name = cls.getName();
		name = (new StringBuilder(String.valueOf(name.replaceAll("\\.", "/")))).append(".class").toString();
		JarEntry en = jarFile.getJarEntry(name);
		if (en == null) {
			jarFile.close();
			throw new ClassNotFoundException(name);
		} else {
			buffer = new byte[(int)en.getSize()];
			bis = new BufferedInputStream(jarFile.getInputStream(en));
			try {
				bis.read(buffer);
			} catch (IOException e) {
				throw e;
			} finally {
				bis.close();
				jarFile.close();
			}
		}
		return buffer;
	}

}
